#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

enum {
    STATE_IDLE = 0,
    STATE_TERMINAL,

    //Tag
    STATE_TAG_BEGIN,// '<'
    STATE_TAG_END,

    //Meta <? ... ?>
    STATE_MTAG_BEGIN,// '?'
    STATE_MTAG_IN,
    STATE_MTAG_END,// '?'

    //Right Tag
    STATE_RTAG_BEGIN,// '/'
    STATE_RTAG_IN,
    STATE_RTAG_END,// '>'

    //Left Tag
    STATE_LTAG_BEGIN,// normal char
    STATE_LTAG_IN,
    STATE_LTAG_EMPTY,// '/'
    STATE_LTAG_END,// '>'

    //Meta property
    //Property name
    STATE_MTAG_PROP_BEGIN_NAME,// ' '
    STATE_MTAG_PROP_IN_NAME,
    STATE_MTAG_PROP_END_NAME,// '='
    //Property value without quote
    STATE_MTAG_PROP_BEGIN_VALUE,// '='
    STATE_MTAG_PROP_IN_VALUE,
    //Property value string with quote
    STATE_MTAG_PROP_BEGIN_VALUE_STR,// '"'
    STATE_MTAG_PROP_IN_VALUE_STR,// '"'
    STATE_MTAG_PROP_END_VALUE_STR,// '"'

    //Noraml Property
    //Property name
    STATE_LTAG_PROP_BEGIN_NAME,// ' '
    STATE_LTAG_PROP_IN_NAME,
    STATE_LTAG_PROP_END_NAME,// '='
    //Property value without quote
    STATE_LTAG_PROP_BEGIN_VALUE,// '='
    STATE_LTAG_PROP_IN_VALUE,
    //Property value string with quote
    STATE_LTAG_PROP_BEGIN_VALUE_STR,// '"'
    STATE_LTAG_PROP_IN_VALUE_STR,// '"'
    STATE_LTAG_PROP_END_VALUE_STR,// '"'
};

int isvalidxmlchar(char c)
{
    if (('0' <= c) && (c <= '9')) {
        return 1;
    }

    if (('a' <= c) && (c <= 'z')) {
        return 1;
    }

    if (('A' <= c) && (c <= 'Z')) {
        return 1;
    }

    if ((c == '-') || (c == '_')) {
        return 1;
    }

    return 0;
}

int isvalidxmlvalue(char c)
{
    if ((c != '/') && (c != '<') && (c != '>')) {
        return 1;
    }

    return 0;
}

void process_xml(FILE* fp)
{
    int curr_state = STATE_IDLE;
    int next_state = STATE_IDLE;
    char buffer[1024] = {0};
    int  i = 0;
    char c = 0;

    while(!feof(fp)) {
        //printf("---new round:state %d---\n", curr_state);
        //Check state
        if (curr_state == STATE_TERMINAL) {
            printf("Terminal at %s:%d\n", __FILE__,__LINE__);
            break;
        }

        c = fgetc(fp);
        //Calculate next state
        if (curr_state == STATE_IDLE) {
            if (c == '<') {
                printf("'%c', next_state STATE_TAG_BEGIN\n", c);
                next_state = STATE_TAG_BEGIN;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_IDLE\n", c);
                next_state = curr_state;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_TAG_BEGIN) {
            if (c == '?') {
                printf("'%c', next_state STATE_MTAG_BEGIN\n", c);
                next_state = STATE_MTAG_BEGIN;
            } else if (c == '/') {
                printf("'%c', next_state STATE_RTAG_BEGIN\n", c);
                next_state = STATE_RTAG_BEGIN;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_TAG_BEGIN\n", c);
                next_state = STATE_TAG_BEGIN;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_LTAG_BEGIN\n", c);
                next_state = STATE_LTAG_BEGIN;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } 
        ////////////////////////////////////////////////////////////////////////
        else if (curr_state == STATE_MTAG_BEGIN) {
            if (isspace(c)) {
                printf("'%c', next_state STATE_MTAG_BEGIN\n", c);
                next_state = curr_state;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_MTAG_IN\n", c);
                next_state = STATE_MTAG_IN;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_MTAG_IN) {
            if (isspace(c)) {
                printf("'%c', next_state STATE_MTAG_PROP_BEGIN_NAME\n", c);
                next_state = STATE_MTAG_PROP_BEGIN_NAME;                
            } else if (c == '?') {
                printf("'%c', next_state STATE_MTAG_END\n", c);
                next_state = STATE_MTAG_END;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_MTAG_IN\n", c);
                next_state = STATE_MTAG_IN;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_MTAG_PROP_BEGIN_NAME) {
            if (isspace(c)) {
                printf("'%c', next_state STATE_MTAG_PROP_BEGIN_NAME\n", c);
                next_state = STATE_MTAG_PROP_BEGIN_NAME;
            } else if (c == '?') {
                printf("'%c', next_state STATE_MTAG_END\n", c);
                next_state = STATE_MTAG_END;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_MTAG_PROP_IN_NAME\n", c);
                next_state = STATE_MTAG_PROP_IN_NAME;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_MTAG_PROP_IN_NAME) {
            if (isspace(c) || (c == '=')) {
                printf("'%c', next_state STATE_MTAG_PROP_END_NAME\n", c);
                next_state = STATE_MTAG_PROP_END_NAME;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_MTAG_PROP_IN_NAME\n", c);
                next_state = STATE_MTAG_PROP_IN_NAME;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_MTAG_PROP_END_NAME) {
            printf("Error at %s:%d\n", __FILE__,__LINE__);
            printf("'%c', next_state STATE_TERMINAL\n", c);
            next_state = STATE_TERMINAL;
        } else if (curr_state == STATE_MTAG_PROP_BEGIN_VALUE) {
            if (c == '"') {
                printf("'%c', next_state STATE_MTAG_PROP_BEGIN_VALUE_STR\n", c);
                next_state = STATE_MTAG_PROP_BEGIN_VALUE_STR;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_MTAG_PROP_IN_VALUE\n", c);
                next_state = STATE_MTAG_PROP_IN_VALUE;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_MTAG_PROP_IN_VALUE) {
            if (c == '"') {
                printf("'%c', next_state STATE_MTAG_PROP_BEGIN_VALUE_STR\n", c);
                next_state = STATE_MTAG_PROP_BEGIN_VALUE_STR;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_MTAG_PROP_IN_VALUE\n", c);
                next_state = STATE_MTAG_PROP_IN_VALUE;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_MTAG_PROP_BEGIN_VALUE_STR) {
            if (c == '"') {
                printf("'%c', next_state STATE_MTAG_PROP_END_VALUE_STR\n", c);
                next_state = STATE_MTAG_PROP_END_VALUE_STR;
            } else if (isvalidxmlvalue(c)) {
                printf("'%c', next_state STATE_MTAG_PROP_IN_VALUE_STR\n", c);
                next_state = STATE_MTAG_PROP_IN_VALUE_STR;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_MTAG_PROP_IN_VALUE_STR) {
            if (c == '"') {
                printf("'%c', next_state STATE_MTAG_PROP_END_VALUE_STR\n", c);
                next_state = STATE_MTAG_PROP_END_VALUE_STR;
            } else if (isvalidxmlvalue(c)) {
                printf("'%c', next_state STATE_MTAG_PROP_IN_VALUE_STR\n", c);
                next_state = STATE_MTAG_PROP_IN_VALUE_STR;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_MTAG_PROP_END_VALUE_STR) {
            printf("Error at %s:%d\n", __FILE__,__LINE__);
            printf("'%c', next_state STATE_TERMINAL\n", c);
            next_state = STATE_TERMINAL;
        } else if (curr_state == STATE_MTAG_END) {
            if (c == '>') {
                printf("'%c', next_state STATE_TAG_END\n", c);
                next_state = STATE_TAG_END;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_MTAG_END\n", c);
                next_state = STATE_MTAG_END;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } 
        ////////////////////////////////////////////////////////////////////////
        else if (curr_state == STATE_LTAG_BEGIN) {
            if (isspace(c)) {
                printf("'%c', next_state STATE_LTAG_BEGIN\n", c);
                next_state = curr_state;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_LTAG_IN\n", c);
                next_state = STATE_LTAG_IN;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_LTAG_IN) {
            if (isspace(c)) {
                printf("'%c', next_state STATE_LTAG_PROP_BEGIN_NAME\n", c);
                next_state = STATE_LTAG_PROP_BEGIN_NAME;                
            } else if (c == '>') {
                printf("'%c', next_state STATE_LTAG_END\n", c);
                next_state = STATE_LTAG_END;
            } else if (c == '/') {
                printf("'%c', next_state STATE_LTAG_EMPTY\n", c);
                next_state = STATE_LTAG_EMPTY;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_LTAG_IN\n", c);
                next_state = STATE_LTAG_IN;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_LTAG_PROP_BEGIN_NAME) {
            if (isspace(c)) {
                printf("'%c', next_state STATE_LTAG_PROP_BEGIN_NAME\n", c);
                next_state = STATE_LTAG_PROP_BEGIN_NAME;
            } else if (c == '>') {
                printf("'%c', next_state STATE_LTAG_END\n", c);
                next_state = STATE_LTAG_END;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_LTAG_PROP_IN_NAME\n", c);
                next_state = STATE_LTAG_PROP_IN_NAME;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_LTAG_PROP_IN_NAME) {
            if (isspace(c) || (c == '=')) {
                printf("'%c', next_state STATE_LTAG_PROP_END_NAME\n", c);
                next_state = STATE_LTAG_PROP_END_NAME;
            } else if (isvalidxmlchar(c)) {
                printf("'%c', next_state STATE_LTAG_PROP_IN_NAME\n", c);
                next_state = STATE_LTAG_PROP_IN_NAME;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_LTAG_PROP_END_NAME) {
            printf("Error at %s:%d\n", __FILE__,__LINE__);
            printf("'%c', next_state STATE_TERMINAL\n", c);
            next_state = STATE_TERMINAL;
        } else if (curr_state == STATE_LTAG_PROP_BEGIN_VALUE) {
            if (c == '"') {
                printf("'%c', next_state STATE_LTAG_PROP_BEGIN_VALUE_STR\n", c);
                next_state = STATE_LTAG_PROP_BEGIN_VALUE_STR;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_LTAG_PROP_IN_VALUE\n", c);
                next_state = STATE_LTAG_PROP_IN_VALUE;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_LTAG_PROP_IN_VALUE) {
            if (c == '"') {
                printf("'%c', next_state STATE_LTAG_PROP_BEGIN_VALUE_STR\n", c);
                next_state = STATE_LTAG_PROP_BEGIN_VALUE_STR;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_LTAG_PROP_IN_VALUE\n", c);
                next_state = STATE_LTAG_PROP_IN_VALUE;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_LTAG_PROP_BEGIN_VALUE_STR) {
            if (c == '"') {
                printf("'%c', next_state STATE_LTAG_PROP_END_VALUE_STR\n", c);
                next_state = STATE_LTAG_PROP_END_VALUE_STR;
            } else if (isvalidxmlvalue(c)) {
                printf("'%c', next_state STATE_LTAG_PROP_IN_VALUE_STR\n", c);
                next_state = STATE_LTAG_PROP_IN_VALUE_STR;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_LTAG_PROP_IN_VALUE_STR) {
            if (c == '"') {
                printf("'%c', next_state STATE_LTAG_PROP_END_VALUE_STR\n", c);
                next_state = STATE_LTAG_PROP_END_VALUE_STR;
            } else if (isvalidxmlvalue(c)) {
                printf("'%c', next_state STATE_LTAG_PROP_IN_VALUE_STR\n", c);
                next_state = STATE_LTAG_PROP_IN_VALUE_STR;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } else if (curr_state == STATE_LTAG_PROP_END_VALUE_STR) {
            printf("Error at %s:%d\n", __FILE__,__LINE__);
            printf("'%c', next_state STATE_TERMINAL\n", c);
            next_state = STATE_TERMINAL;
        } else if (curr_state == STATE_LTAG_END) {
            if (c == '>') {
                printf("'%c', next_state STATE_TAG_END\n", c);
                next_state = STATE_TAG_END;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_LTAG_END\n", c);
                next_state = STATE_LTAG_END;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        } 
        else if (curr_state == STATE_LTAG_EMPTY) {
            if (c == '>') {
                printf("'%c', next_state STATE_LTAG_END\n", c);
                next_state = STATE_LTAG_END;
            } else if (isspace(c)) {
                printf("'%c', next_state STATE_LTAG_EMPTY\n", c);
                next_state = STATE_LTAG_EMPTY;
            } else {
                printf("'%c', next_state STATE_TERMINAL\n", c);
                next_state = STATE_TERMINAL;
            }
        }
        ////////////////////////////////////////////////////////////////////////
        else if (curr_state == STATE_TAG_END) {
            printf("Error at %s:%d\n", __FILE__,__LINE__);
            printf("'%c', next_state STATE_TERMINAL\n", c);
            next_state = STATE_TERMINAL;
        }

        //Doing something
        //Save chars
        if (next_state == STATE_MTAG_IN){
            buffer[i++] = c;
        }
        if (next_state == STATE_MTAG_PROP_IN_NAME) {
            buffer[i++] = c;
        }
        if (next_state == STATE_MTAG_PROP_IN_VALUE_STR) {
            buffer[i++] = c;
        }
        ///
        if ((next_state == STATE_LTAG_BEGIN) || (next_state == STATE_LTAG_IN)){
            buffer[i++] = c;
        }
        if (next_state == STATE_LTAG_PROP_IN_NAME) {
            buffer[i++] = c;
        }
        if (next_state == STATE_LTAG_PROP_IN_VALUE_STR) {
            buffer[i++] = c;
        }
        
        //Print
        if ((i > 0) && (curr_state == STATE_MTAG_IN) && 
                (next_state != STATE_MTAG_IN)) {
            buffer[i++] = '\0';
            i = 0;
            printf("\tTag : \"%s\"\n", buffer);
        }
        if ((curr_state == STATE_MTAG_PROP_IN_NAME) && 
                (next_state != STATE_MTAG_PROP_IN_NAME)) {
            buffer[i++] = '\0';
            i = 0;
            printf("\tProp: \"%s\"\n", buffer);
        }
        if ((curr_state == STATE_MTAG_PROP_IN_VALUE_STR) && 
                (next_state != STATE_MTAG_PROP_IN_VALUE_STR)) {
            buffer[i++] = '\0';
            i = 0;
            printf("\tPval: \"%s\"\n", buffer);
        }
        ///
        if ((i > 0) && (curr_state == STATE_LTAG_IN) && 
                (next_state != STATE_LTAG_IN)) {
            buffer[i++] = '\0';
            i = 0;
            printf("\tTag : \"%s\"\n", buffer);
        }
        if ((curr_state == STATE_LTAG_PROP_IN_NAME) && 
                (next_state != STATE_LTAG_PROP_IN_NAME)) {
            buffer[i++] = '\0';
            i = 0;
            printf("\tProp: \"%s\"\n", buffer);
        }
        if ((curr_state == STATE_LTAG_PROP_IN_VALUE_STR) && 
                (next_state != STATE_LTAG_PROP_IN_VALUE_STR)) {
            buffer[i++] = '\0';
            i = 0;
            printf("\tPval: \"%s\"\n", buffer);
        }

        //Modify next state
        if (next_state == STATE_TAG_END) {
            //printf("next_state modify to STATE_IDLE\n");
            next_state = STATE_IDLE;
        }
        ///
        if (next_state == STATE_MTAG_PROP_END_NAME) {
            //printf("next_state modify to STATE_MTAG_PROP_BEGIN_VALUE\n");
            next_state = STATE_MTAG_PROP_BEGIN_VALUE;
        }
        if (next_state == STATE_MTAG_PROP_END_VALUE_STR) {
            //printf("next_state modify to STATE_MTAG_IN\n");
            next_state = STATE_MTAG_IN;
        }
        ///
        if (next_state == STATE_LTAG_END) {
            //printf("next_state modify to STATE_IDLE\n");
            next_state = STATE_IDLE;
        }
        if (next_state == STATE_LTAG_PROP_END_NAME) {
            //printf("next_state modify to STATE_LTAG_PROP_BEGIN_VALUE\n");
            next_state = STATE_LTAG_PROP_BEGIN_VALUE;
        }
        if (next_state == STATE_LTAG_PROP_END_VALUE_STR) {
            //printf("next_state modify to STATE_LTAG_IN\n");
            next_state = STATE_LTAG_IN;
        }

        //Update next state
        curr_state = next_state;
        //printf("---next round:state %d---\n\n", curr_state);
    }//end of while(!feof(fp)) {

    return;
}

int main(int argc, char* argv[])
{
    FILE* fp = NULL;

    if (argc != 2) {
        printf("Need input file\n");
        return -1;
    } 

    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("Can't open file.");
        return -1;
    }

    process_xml(fp);

    fclose(fp);
    fp = NULL;

    return 0;
}

